import * as crypto from 'crypto';
import axios from 'axios';

/**
 * 微信小程序 API 工具类
 */
export class WechatUtil {
  /**
   * 获取微信小程序 access_token（用于获取 openid）
   * @param appId 小程序 AppID
   * @param appSecret 小程序 AppSecret
   * @param code 登录凭证 code
   * @returns { openid, session_key, unionid }
   */
  static async code2Session(
    appId: string,
    appSecret: string,
    code: string,
  ): Promise<{
    openid: string;
    session_key: string;
    unionid?: string;
    errcode?: number;
    errmsg?: string;
  }> {
    const url = 'https://api.weixin.qq.com/sns/jscode2session';

    try {
      const response = await axios.get(url, {
        params: {
          appid: appId,
          secret: appSecret,
          js_code: code,
          grant_type: 'authorization_code',
        },
      });

      return response.data;
    } catch (error) {
      throw new Error(`微信 code2Session 请求失败: ${error.message}`);
    }
  }

  /**
   * 解密微信加密数据（手机号等）
   * @param encryptedData 加密数据
   * @param iv 初始向量
   * @param sessionKey 会话密钥
   * @returns 解密后的数据
   */
  static decryptData(
    encryptedData: string,
    iv: string,
    sessionKey: string,
  ): any {
    try {
      // Base64 decode
      const sessionKeyBuffer = Buffer.from(sessionKey, 'base64');
      const encryptedDataBuffer = Buffer.from(encryptedData, 'base64');
      const ivBuffer = Buffer.from(iv, 'base64');

      // 创建解密器
      const decipher = crypto.createDecipheriv(
        'aes-128-cbc',
        sessionKeyBuffer,
        ivBuffer,
      );

      // 设置自动 padding 为 true，删除填充
      decipher.setAutoPadding(true);

      // 解密
      let decrypted = decipher.update(encryptedDataBuffer, undefined, 'utf8');
      decrypted += decipher.final('utf8');

      // 解析 JSON
      const decryptedData = JSON.parse(decrypted);

      return decryptedData;
    } catch (error) {
      throw new Error(`微信数据解密失败: ${error.message}`);
    }
  }

  /**
   * 解密手机号
   * @param encryptedData 加密数据
   * @param iv 初始向量
   * @param sessionKey 会话密钥
   * @returns { phoneNumber, purePhoneNumber, countryCode }
   */
  static decryptPhoneNumber(
    encryptedData: string,
    iv: string,
    sessionKey: string,
  ): {
    phoneNumber: string;
    purePhoneNumber: string;
    countryCode: string;
  } {
    const decryptedData = this.decryptData(encryptedData, iv, sessionKey);

    if (!decryptedData.phoneNumber) {
      throw new Error('解密的数据中未找到手机号');
    }

    return {
      phoneNumber: decryptedData.phoneNumber,
      purePhoneNumber: decryptedData.purePhoneNumber,
      countryCode: decryptedData.countryCode,
    };
  }

  /**
   * 验证数据签名
   * @param rawData 原始数据
   * @param sessionKey 会话密钥
   * @param signature 签名
   * @returns 是否验证通过
   */
  static verifySignature(
    rawData: string,
    sessionKey: string,
    signature: string,
  ): boolean {
    const hash = crypto
      .createHash('sha1')
      .update(rawData + sessionKey)
      .digest('hex');

    return hash === signature;
  }

  /**
   * 获取小程序 Access Token
   * @param appId 小程序 AppID
   * @param appSecret 小程序 AppSecret
   * @returns Access Token
   */
  static async getAccessToken(
    appId: string,
    appSecret: string,
  ): Promise<string> {
    const url = 'https://api.weixin.qq.com/cgi-bin/token';

    try {
      const response = await axios.get(url, {
        params: {
          grant_type: 'client_credential',
          appid: appId,
          secret: appSecret,
        },
      });

      if (response.data.errcode) {
        throw new Error(
          `获取 Access Token 失败: ${response.data.errcode} - ${response.data.errmsg}`,
        );
      }

      return response.data.access_token;
    } catch (error) {
      throw new Error(`获取 Access Token 请求失败: ${error.message}`);
    }
  }

  /**
   * 发送订阅消息
   * @param accessToken Access Token
   * @param options 订阅消息选项
   * @returns 发送结果
   */
  static async sendSubscribeMessage(
    accessToken: string,
    options: {
      touser: string; // 接收者 openid
      template_id: string; // 订阅消息模板 ID
      page?: string; // 点击消息跳转的页面
      data: Record<string, { value: string }>; // 模板数据
      miniprogram_state?: 'developer' | 'trial' | 'formal'; // 跳转小程序类型
    },
  ): Promise<{
    errcode: number;
    errmsg: string;
  }> {
    const url = `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${accessToken}`;

    try {
      const response = await axios.post(url, {
        touser: options.touser,
        template_id: options.template_id,
        page: options.page,
        data: options.data,
        miniprogram_state: options.miniprogram_state || 'formal',
      });

      return response.data;
    } catch (error) {
      throw new Error(`发送订阅消息失败: ${error.message}`);
    }
  }
}
